use std::ops::{Add, Deref};
use std::rc::Rc;
use chrono::{DateTime, Local, TimeDelta};
use yew::UseReducerHandle;
use crate::db::pojo::pojo::{ImConfig, ImDepartment, ImGroup, ImModule, ImSession, ImSetting, ImUser};
use crate::db::service::im_setting_service::ImSettingService;
use crate::business::app_state::AppState;
use crate::business::page::login_page:: {LoadDataReducer, LoadDataAction};
use crate::business::websocket::dto::im_message_dto::MessageObject;
use crate::business::websocket::service::pull_end_callback::PullEndCallback;
use crate::business::websocket::service::rpc_pull_service::{syn_configs, syn_departments, syn_groups, syn_messages, syn_modules, syn_sessions, syn_users};
use crate::util::time::{format_time_by_format, parse_to_time_by_format};

pub fn syn_data(app_state: AppState, user_id: String, load_data_reducer: UseReducerHandle<LoadDataReducer>) {
    log::info!("syn_data started, user_id: {}", &user_id);
    // let start_time = Local::now();
    syn_common(app_state, user_id, load_data_reducer.clone());
}

fn syn_common(app_state: AppState, user_id: String, load_data_reducer: UseReducerHandle<LoadDataReducer>) {
    let syn_data_config:SynDataConfig<ImConfig> = SynData::new(app_state, user_id, "SynConfig", load_data_reducer.clone()).into();

    syn_data_wrap(syn_data_config, |syn_data_config| {
        load_data_reducer.dispatch(LoadDataAction::SendMsg("开始同步配置信息".to_string()));

        syn_configs(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_configs_end)});
    });
}

fn syn_configs_end(syn_data_config: SynDataConfig<ImConfig>) {
    log::info!("syn config pull is end, {}", &syn_data_config.is_end);

    let load_data_reducer = syn_data_config.load_data_reducer.clone();
    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name);
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(5.0, "开始同步结构信息...".to_string()));

        let department_load_data_config: SynDataConfig<ImDepartment> = syn_data_config.to("ImDepartment");

        syn_data_wrap(department_load_data_config, |syn_data_config_department| {
            syn_departments(syn_data_config_department.clone(), PullEndCallback{callback: Rc::new(syn_department_end)});
        });
    } else {
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(2.0, "继续同步配置信息...".to_string()));

        syn_configs(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_configs_end)});
    }
}

fn syn_department_end(syn_data_config: SynDataConfig<ImDepartment>) {
    log::info!("syn department pull is end, {}, {}", syn_data_config.is_end, syn_data_config.user_id);
    let load_data_reducer = syn_data_config.load_data_reducer.clone();

    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name.clone());
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(10.0, "开始同步用户信息...".to_string()));
        // 加载其他数据

        let user_load_data_config: SynDataConfig<ImUser> = syn_data_config.to("SynUser");
        syn_data_wrap(user_load_data_config, |syn_data_config_user2| {
            syn_users(syn_data_config_user2.clone(), PullEndCallback{callback: Rc::new(syn_user_end)});
        });
    } else {
        syn_departments(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_department_end)});
    }
}

fn syn_user_end(syn_data_config: SynDataConfig<ImUser>) {
    log::info!("syn user pull is end, {}", syn_data_config.is_end);

    let load_data_reducer = syn_data_config.load_data_reducer.clone();

    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name);
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(20.0, "开始同步群信息...".to_string()));

        // 加载其他数据
        let group_load_data_config: SynDataConfig<ImGroup> = syn_data_config.to("SynGroup");
        syn_data_wrap(group_load_data_config, |syn_data_config_group| {
            syn_groups(syn_data_config_group.clone(), PullEndCallback{callback: Rc::new(syn_group_end)});
        });

    } else {
        if syn_data_config.lasted_data.is_none() {
            return;
        }
        syn_users(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_user_end)});
    }
}

fn syn_group_end(syn_data_config: SynDataConfig<ImGroup>) {
    log::info!("syn group pull is end, {}", syn_data_config.is_end);
    let load_data_reducer = syn_data_config.load_data_reducer.clone();

    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name);
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(20.0, "开始同步模块信息...".to_string()));
        // 加载其他数据
        let module_load_data_config: SynDataConfig<ImModule> = syn_data_config.to("SynModule");
        syn_data_wrap(module_load_data_config, |syn_data_config_module| {
            syn_modules(syn_data_config_module.clone(), PullEndCallback{callback: Rc::new(syn_module_end)});
        });

    } else {
        if syn_data_config.lasted_data.is_none() {
            return;
        }
        syn_groups(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_group_end)});
    }
}

fn syn_module_end(syn_data_config: SynDataConfig<ImModule>) {
    log::info!("syn module pull is end, {}", syn_data_config.is_end);
    let load_data_reducer = syn_data_config.load_data_reducer.clone();

    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name);
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(10.0, "开始同步会话信息...".to_string()));

        // 加载其他数据
        let session_load_data_config: SynDataConfig<ImSession> = syn_data_config.to("SynSession");
        syn_data_wrap(session_load_data_config, |session_load_data_config2| {
            syn_sessions(session_load_data_config2.clone(), PullEndCallback{callback: Rc::new(syn_session_end)});
        });

    } else {
        if syn_data_config.lasted_data.is_none() {
            return;
        }
        syn_modules(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_module_end)});
    }
}

fn syn_session_end(syn_data_config: SynDataConfig<ImSession>) {
    log::info!("syn session pull is end, {}", syn_data_config.is_end);
    let load_data_reducer = syn_data_config.load_data_reducer.clone();

    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name);
        load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(10.0, "开始同步消息...".to_string()));

        // 加载其他数据
        let message_load_data_config: SynDataConfig<MessageObject> = syn_data_config.to("SynMessage");
        syn_data_wrap(message_load_data_config, |message_load_data_config2| {
            syn_messages(message_load_data_config2.clone(), PullEndCallback{callback: Rc::new(syn_message_end)});
        });

    } else {
        if syn_data_config.lasted_data.is_none() {
            return;
        }
        syn_sessions(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_session_end)});
    }
}

fn syn_message_end(syn_data_config: SynDataConfig<MessageObject>) {
    log::info!("syn message pull is end, {}", syn_data_config.is_end);
    let load_data_reducer = syn_data_config.load_data_reducer.clone();

    if syn_data_config.is_end {
        insert_or_update_setting(syn_data_config.user_id.clone(), syn_data_config.start_time.clone(), syn_data_config.name);
        // load_data_reducer.dispatch(LoadDataAction::SendProgressAndMsg(10.0, "开始同步会话信息...".to_string()));
        load_data_reducer.dispatch(LoadDataAction::Done("同步数据完成".to_string()));

        // 加载其他数据
        // todo

    } else {
        if syn_data_config.lasted_data.is_none() {
            return;
        }
        syn_messages(syn_data_config.clone(), PullEndCallback{callback: Rc::new(syn_message_end)});
    }
}






pub fn syn_data_wrap<F: FnOnce(SynDataConfig<T>), T>(syn_data_config: SynDataConfig<T>, do_business: F) {
    let setting_service = ImSettingService;
    let db_setting = setting_service.get_by_name(&syn_data_config.user_id, syn_data_config.name);
    let (setting_time_str, is_latest_change) = match db_setting {
        None => {(None, false)}
        Some(db_setting) => {(Some(db_setting.value), true)}
    };

    if !is_latest_change {
        let new_syn_data_config = syn_data_config.copy(None);
        do_business(new_syn_data_config);
    } else {
        let mut setting_time = parse_to_time_by_format(&setting_time_str.unwrap(), "%Y-%m-%d %H:%M").unwrap();
        setting_time = setting_time.add(TimeDelta::minutes(-1));

        let new_syn_data_config = syn_data_config.copy(Some(setting_time));
        do_business(new_syn_data_config);
    }
}

fn insert_or_update_setting(current_user_id: String, start_time: Option<DateTime<Local>>, name: &'static str) {
    let setting_service = ImSettingService;
    log::info!("update setting started, {}, {}, {:?}", &current_user_id, name, start_time);
    let mut setting = match start_time {
        None => {ImSetting::new(current_user_id, name.to_string())}
        Some(_start_time) => {
            setting_service.get_by_name(&current_user_id, name).unwrap()
        }
    };
    let now = Local::now().add(chrono::Duration::minutes(-5));
    setting.value = format_time_by_format(&now, "%Y-%m-%d %H:%M");
    if start_time.is_some() {
        setting_service.update(&mut setting);
    } else {
        setting_service.insert(&mut setting);
    }
}

pub struct SynDataConfig<T> {
    pub data: Rc<SynData<T>>,
}

#[derive(Clone)]
pub struct SynData<T> {
    pub user_id: String,
    pub name: &'static str,
    pub start_time: Option<DateTime<Local>>,
    pub lasted_data: Option<T>,
    pub is_end: bool,
    pub app_state: AppState,
    pub load_data_reducer: UseReducerHandle<LoadDataReducer>,
}

impl<T> SynDataConfig<T> {

    fn to<E>(self, name: &'static str) -> SynDataConfig<E> {
        let data = SynData {
            user_id: self.user_id.clone(),
            name,
            start_time: None,
            lasted_data: None,
            is_end: false,
            app_state: self.app_state.clone(),
            load_data_reducer: self.load_data_reducer.clone(),
        };
        SynDataConfig {
            data: Rc::new(data),
        }
    }

    fn copy<E>(self, start_time: Option<DateTime<Local>>) -> SynDataConfig<E> {
        SynDataConfig {
            data: Rc::new(SynData{
                user_id: self.user_id.clone(),
                name: self.name,
                start_time,
                lasted_data: None,
                is_end: self.is_end.clone(),
                app_state: self.app_state.clone(),
                load_data_reducer: self.load_data_reducer.clone(),
            })
        }
    }

    pub(crate) fn copy2<E>(self, lasted_data: Option<E>, is_end: bool) -> SynDataConfig<E> {
        SynDataConfig {
            data: Rc::new(SynData{
                user_id: self.user_id.clone(),
                name: self.name,
                start_time: self.start_time.clone(),
                lasted_data,
                is_end,
                app_state: self.app_state.clone(),
                load_data_reducer: self.load_data_reducer.clone(),
            })
        }
    }
}

impl<T> Deref for SynDataConfig<T> {
    type Target = SynData<T>;

    fn deref(&self) -> &Self::Target {
        self.data.as_ref()
    }
}

impl<T> SynData<T> {
    pub fn new(app_state: AppState, user_id: String, name: &'static str, load_data_reducer: UseReducerHandle<LoadDataReducer>) -> Self {
        Self {
            user_id,
            name,
            start_time: None,
            lasted_data: None,
            is_end: false,
            app_state,
            load_data_reducer,
        }
    }
}

impl<T> From<SynData<T>> for SynDataConfig<T> {
    fn from(value: SynData<T>) -> Self {
        Self {
            data: Rc::new(value)
        }
    }
}

impl<T> Clone for SynDataConfig<T> {
    fn clone(&self) -> Self {
        Self {
            data: self.data.clone(),
        }
    }
}

